home *** CD-ROM | disk | FTP | other *** search
Text File | 1986-10-31 | 57.8 KB | 1,031 lines |
- (*----------------------------------------------------------------------*)
- (* PIBASYNC.PAS --- Asynchronous I/O for Turbo Pascal *)
- (*----------------------------------------------------------------------*)
- (* *)
- (* Author: (c) 1985, 1986 by Philip R. Burns *)
- (* *)
- (* Version: 1.0 (January, 1985) *)
- (* 2.0 (June, 1985) *)
- (* 2.1 (July, 1985) *)
- (* 3.0 (October, 1985) *)
- (* 3.1 (October, 1985) *)
- (* 3.2 (November, 1985) *)
- (* 4.0 (October, 1986) *)
- (* *)
- (* Systems: For MSDOS/PC DOS on IBM PCs and close compatibles only. *)
- (* *)
- (* History: Some of these routines are based upon ones written by: *)
- (* *)
- (* Alan Bishop *)
- (* C. J. Dunford *)
- (* Michael Quinlan *)
- (* Gene Harris *)
- (* *)
- (* I have cleaned up these other authors' code, fixed some *)
- (* bugs, and added many new features. *)
- (* *)
- (* In particular, starting with v4.0 of PibTerm, both input *)
- (* and output to the serial port is buffered and interrupt- *)
- (* driven. Also, XON/XOFF support has been moved to the *)
- (* serial port interrupt handler, which results in fewer *)
- (* overrun problems. *)
- (* *)
- (* Suggestions for improvements or corrections are welcome. *)
- (* *)
- (* If you use this code in your own programs, please be nice *)
- (* and give proper credit. *)
- (* *)
- (*----------------------------------------------------------------------*)
- (* *)
- (* Routines: *)
- (* *)
- (* BIOS_RS232_Init --- Use BIOS to initialize port *)
- (* DOS_Set_Intrpt --- Set interrupt vector using DOS *)
- (* DOS_Get_Intrpt --- Get interrupt vector using DOS *)
- (* Async_Isr --- Com port interrupt service routine *)
- (* Async_Init --- Performs initialization. *)
- (* Async_Clear_Errors --- Clear pending serial port errors *)
- (* Async_Reset_Port --- Resets UART parameters for port *)
- (* Async_Open --- Sets up COM port *)
- (* Async_Close --- Closes down COM port *)
- (* Async_Carrier_Detect --- Checks for modem carrier detect *)
- (* Async_Carrier_Drop --- Checks for modem carrier drop *)
- (* Async_Buffer_Check --- Checks if character in COM buffer *)
- (* Async_Term_Ready --- Toggles terminal ready status *)
- (* Async_Receive --- Reads character from COM buffer *)
- (* Async_Receive_With_Timeout *)
- (* --- Receives char. with timeout check *)
- (* Async_Ring_Detect --- If ringing detected *)
- (* Async_Send --- Transmits char over COM port *)
- (* Async_Send_String --- Sends string over COM port *)
- (* Async_Send_String_With_Delays *)
- (* --- Sends string with timed delays *)
- (* Async_Send_Break --- Sends break (attention) signal *)
- (* Async_Percentage_Used --- Returns percentage com buffer used *)
- (* Async_Purge_Buffer --- Purges receive buffer *)
- (* Async_Release_Buffers --- Free memory for serial port queues *)
- (* Async_Setup_Port --- Define port base, IRQ, RS232 addr *)
- (* Async_Stuff --- Insert char into receive buffer *)
- (* *)
- (*----------------------------------------------------------------------*)
- (* *)
- (* PIBASYNC.PAS was split into PIBASYN1.PAS and PIBASYN2.PAS at *)
- (* version 3.2 of PibTerm, and into PIBASYN1, PIBASYN2, and *)
- (* PIBASYN3 for v4.0 of PibTerm. *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- (*----------------------------------------------------------------------*)
- (* BIOS_RS232_Init --- Initialize UART *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE BIOS_RS232_Init( ComPort, ComParm : INTEGER );
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: BIOS_RS232_Init *)
- (* *)
- (* Purpose: Issues interrupt $14 to initialize the UART *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* BIOS_RS232_Init( ComPort, ComParm : INTEGER ); *)
- (* *)
- (* ComPort --- Communications Port Number (0 thru 3) *)
- (* ComParm --- Communications Parameter Word *)
- (* *)
- (* Calls: INTR (to perform BIOS interrupt $14) *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- VAR
- Regs: RegPack;
- Save: INTEGER;
-
- BEGIN (* BIOS_RS232_Init *)
- (* Set RS 232 base. *)
-
- Save := MEM[$0:RS232_Base];
- MEM[$0:RS232_Base] := Async_RS232;
-
- (* Initialize port *)
- WITH Regs DO
- BEGIN
- Ax := ComParm AND $00FF; (* AH=0; AL=ComParm *)
- Dx := ComPort; (* Port number to use *)
- INTR($14, Regs);
- END;
- (* Set RS 232 base back *)
- MEM[$0:RS232_Base] := Save;
-
- END (* BIOS_RS232_Init *);
-
- (*----------------------------------------------------------------------*)
- (* DOS_Set_Intrpt --- Call DOS to set interrupt vector *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE DOS_Set_Intrpt( V, S, O : INTEGER );
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: DOS_Set_Intrpt *)
- (* *)
- (* Purpose: Calls DOS to set interrupt vector *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* DOS_Set_Intrpt( V, S, O : INTEGER ); *)
- (* *)
- (* V --- interrupt vector number to set *)
- (* S --- segment address of interrupt routine *)
- (* O --- offset address of interrupt routine *)
- (* *)
- (* Calls: MSDOS (to set interrupt) *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- VAR
- Regs : Regpack;
-
- BEGIN (* DOS_Set_Intrpt *)
-
- WITH Regs DO
- BEGIN
- Ax := $2500 + ( V AND $00FF );
- Ds := S;
- Dx := O;
- Es := 0;
- MsDos( Regs );
- END;
-
- END (* DOS_Set_Intrpt *);
-
- (*----------------------------------------------------------------------*)
- (* DOS_Get_Intrpt --- Call DOS to get interrupt vector *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE DOS_Get_Intrpt( V: INTEGER; VAR S: INTEGER; VAR O : INTEGER );
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: DOS_Get_Intrpt *)
- (* *)
- (* Purpose: Calls DOS to get interrupt vector *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* DOS_Get_Intrpt( V, S, O : INTEGER ); *)
- (* *)
- (* V --- interrupt vector number to get *)
- (* S --- segment address of interrupt routine *)
- (* O --- offset address of interrupt routine *)
- (* *)
- (* Calls: MSDOS (to get interrupt) *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- VAR
- Regs : Regpack;
-
- BEGIN (* DOS_Get_Intrpt *)
-
- Regs.Ax := $3500 + ( V AND $00FF );
-
- MsDos( Regs );
-
- S := Regs.Es;
- O := Regs.Bx;
-
- END (* DOS_Get_Intrpt *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Isr --- Interrupt Service Routine *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE Async_Isr;
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: Async_Isr *)
- (* *)
- (* Purpose: Invoked when serial port interrupt occurs. *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Async_Isr; *)
- (* *)
- (* --- Called asynchronously only!!!!!! *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- BEGIN (* Async_Isr *)
-
- INLINE(
- $FB { STI ;Allow interrupts}
- {;}
- /$50 { PUSH AX ;Save registers}
- /$53 { PUSH BX}
- /$51 { PUSH CX}
- /$52 { PUSH DX}
- /$1E { PUSH DS}
- /$56 { PUSH SI}
- /$06 { PUSH ES}
- /$57 { PUSH DI}
- {;}
- {; Get data segment for addressing global variables}
- {;}
- /$2E/$8E/$1E/>ASYNC_DSEG_SAVE { CS: MOV DS,[>Async_DSeg_Save]}
- {;}
- {; Begin major polling loop over pending interrupts.}
- {;}
- {; The polling loop is needed because the 8259 cannot handle another 8250}
- {; interrupt while we service this interrupt. We keep polling here as long}
- {; as an interrupt is received.}
- {;}
- /$8B/$16/>ASYNC_UART_IIR {Poll: MOV DX,[>Async_Uart_IIR] ;Get Interrupt ident register}
- /$EC { IN AL,DX ;Pick up interrupt type}
- {;}
- /$A8/$01 { TEST AL,1 ;See if any interrupt signalled.}
- /$74/$03 { JZ Polla ;Yes --- continue}
- /$E9/$7F/$01 { JMP NEAR Back ;No --- return to invoker}
- {;}
- {; Determine type of interrupt.}
- {; Possibilities:}
- {;}
- {; 0 = Modem status changed}
- {; 2 = Transmit hold register empty (write char)}
- {; 4 = Character received from port}
- {; 6 = Line status changed}
- {;}
- /$24/$06 {Polla: AND AL,6 ;Strip unwanted bits from interrupt type}
- /$3C/$04 { CMP AL,4 ;Check if interrupt >= 4}
- /$74/$03 { JE Pollb ;}
- /$E9/$A1/$00 { JMP NEAR Int2}
- {;}
- {; Write interrupts must be turned on if a higher-priority interrupt}
- {; has been received, else the characters may not be sent (and a lockup}
- {; may occur).}
- {;}
- /$50 {Pollb: PUSH AX ;Save interrupt type}
- /$E8/$65/$01 { CALL EnabWI ;Enable write interrupts}
- /$58 { POP AX ;Restore interrupt type}
- {;}
- {; --- Received a character ----}
- {;}
- /$3C/$04 {Int4: CMP AL,4 ;Check for received char interrupt}
- /$74/$03 { JE Int4a ;Yes -- process it.}
- /$E9/$95/$00 { JMP NEAR Int2 ;No -- skip.}
- {;}
- {; Read the character from the serial port.}
- {;}
- /$8B/$16/>ASYNC_BASE {Int4a: MOV DX,[>Async_Base] ;Read character from port}
- /$EC { IN AL,DX}
- {;}
- {; Check if XON/XOFF honored. If so, check if incoming character is}
- {; an XON or an XOFF.}
- {;}
- /$F6/$06/>ASYNC_DO_XONXOFF/$01 { TEST BYTE [<Async_Do_XonXoff],1 ;See if we honor XON/XOFF}
- /$74/$25 { JZ Int4d ;No -- skip XON/XOFF checks}
- {;}
- /$3C/<XON { CMP AL,<XON ;See if XON found}
- /$74/$11 { JE Int4b ;Skip if XON found}
- /$3C/<XOFF { CMP AL,<XOFF ;See if XOFF found}
- /$75/$1D { JNE Int4d ;Skip if XOFF not found}
- {;}
- {; XOFF received -- set flag indicating sending of chars isn't possible}
- {;}
- /$C6/$06/>ASYNC_XOFF_RECEIVED/$01 { MOV BYTE [<Async_XOFF_Received],1 ;Turn on received XOFF flag}
- /$C6/$06/>ASYNC_XOFF_REC_DISPLAY/$01{ MOV BYTE [<Async_XOFF_Rec_Display],1 ;Turn on display flag}
- /$E9/$BE/$FF { JMP NEAR Poll}
- {;}
- {; XON received -- allow more characters to be sent.}
- {;}
- /$C6/$06/>ASYNC_XOFF_RECEIVED/$00 {Int4b: MOV BYTE [<Async_XOFF_Received],0 ;Turn off received XOFF flag}
- /$C6/$06/>ASYNC_XON_REC_DISPLAY/$01{ MOV BYTE [<Async_XON_Rec_Display],1 ;Turn on display flag}
- {;}
- /$E8/$2F/$01 { CALL EnabWI ;Enable write interrupts}
- /$E9/$61/$00 { JMP NEAR Int4z}
- {;}
- {; Not XON/XOFF -- handle other character.}
- {;}
- /$F6/$06/>ASYNC_LINE_STATUS/$02{Int4d: TEST BYTE [>Async_Line_Status],2 ;Check for buffer overrun}
- /$75/$5A { JNZ Int4z ;Yes --- don't store anything}
- {;}
- /$8B/$1E/>ASYNC_BUFFER_HEAD { MOV BX,[>Async_Buffer_Head] ;Current position in input buffer}
- /$C4/$3E/>ASYNC_BUFFER_PTR { LES DI,[>Async_Buffer_Ptr] ;Pick up buffer address}
- /$01/$DF { ADD DI,BX ;Update position}
- /$26/$88/$05 { ES: MOV [DI],AL ;Store received character in buffer}
- /$FF/$06/>ASYNC_BUFFER_USED { INC WORD [>Async_Buffer_Used] ;Increment count of chars in buffer}
- {;}
- /$A1/>ASYNC_BUFFER_USED { MOV AX,[>Async_Buffer_Used] ;Pick up buffer usage count}
- /$3B/$06/>ASYNC_MAXBUFFERUSED { CMP AX,[>Async_MaxBufferUsed] ;See if greater usage than ever before}
- /$7E/$03 { JLE Int4f ;Skip if not}
- /$A3/>ASYNC_MAXBUFFERUSED { MOV [>Async_MaxBufferUsed],AX ;This is greatest use thus far}
- {;}
- /$43 {Int4f: INC BX ;Increment buffer pointer}
- /$3B/$1E/>ASYNC_BUFFER_SIZE { CMP BX,[>Async_Buffer_Size] ;Check if past end of buffer}
- /$7E/$02 { JLE Int4h}
- /$31/$DB { XOR BX,BX ;If so, wrap around to front}
- {;}
- /$39/$1E/>ASYNC_BUFFER_TAIL {Int4h: CMP WORD [>Async_Buffer_Tail],BX ;Check for overflow}
- /$74/$29 { JE Int4s ;Jump if head ran into tail}
- {;}
- /$89/$1E/>ASYNC_BUFFER_HEAD { MOV [>Async_Buffer_Head],BX ;Update head pointer}
- {;}
- {; If XON/XOFF available, and buffer getting full, set up to send}
- {; XOFF to remote system.}
- {;}
- {; This happens in two possible stages:}
- {;}
- {; (1) An XOFF is sent right when the buffer becomes 'Async_Buffer_High'}
- {; characters full.}
- {;}
- {; (2) A second XOFF is sent right when the buffer becomes}
- {; 'Async_Buffer_High_2' characters full; this case is likely the}
- {; result of the remote not having seen our XOFF because it was}
- {; lost in transmission.}
- {;}
- /$F6/$06/>ASYNC_DO_XONXOFF/$01 { TEST BYTE [<Async_Do_XonXoff],1 ;See if we honor XON/XOFF}
- /$74/$23 { JZ Int4z ;No -- skip XON/XOFF checks}
- {;}
- {; Check against first high-water mark.}
- {;}
- /$3B/$06/>ASYNC_BUFFER_HIGH { CMP AX,[>Async_Buffer_High] ;AX still has Async_Buffer_Used}
- /$7C/$1D { JL Int4z ;Not very full, so keep going.}
- {;}
- {; Check if we've already sent XOFF.}
- {;}
- /$F6/$06/>ASYNC_XOFF_SENT/$01 { TEST BYTE [<Async_XOFF_Sent],1 ;Remember if we sent XOFF or not}
- /$74/$06 { JZ Int4j ;No -- go send it now.}
- {;}
- {; Check against second high-water mark.}
- {; If we are right at it, send an XOFF regardless of whether we've}
- {; already sent one or not. (Perhaps the first got lost.)}
- {;}
- /$3B/$06/>ASYNC_BUFFER_HIGH_2 { CMP AX,[>Async_Buffer_High_2]}
- /$75/$10 { JNE Int4z ;Not at 2nd mark -- skip}
- {;}
- /$C6/$06/>ASYNC_SEND_XOFF/$01 {Int4j: MOV BYTE [<Async_Send_XOFF],1 ;Indicate we need to send XOFF}
- /$E8/$D3/$00 { CALL EnabWI ;Ensure write interrupts enabled}
- /$E9/$52/$FF { JMP NEAR Poll ;}
- {;}
- {; If we come here, then the input buffer has overflowed.}
- {; Characters will be thrown away until the buffer empties at least one slot.}
- {;}
- /$80/$0E/>ASYNC_LINE_STATUS/$02 {Int4s: OR BYTE PTR [>Async_Line_Status],2 ;Flag overrun}
- {;}
- /$E9/$4A/$FF {Int4z: JMP NEAR Poll}
- {;}
- {; --- Write a character ---}
- {;}
- /$3C/$02 {Int2: CMP AL,2 ;Check for THRE interrupt}
- /$74/$03 { JE Int2a ;Yes -- process it.}
- /$E9/$97/$00 { JMP NEAR Int6 ;No -- skip.}
- {;}
- {; Check first if we need to send an XOFF to remote system.}
- {;}
- /$F6/$06/>ASYNC_SEND_XOFF/$01 {Int2a: TEST BYTE [<Async_Send_Xoff],1 ;See if we are sending XOFF}
- /$74/$34 { JZ Int2d ;No -- skip it}
- {;}
- {; Yes, we are to send XOFF to remote.}
- {;}
- {; First, check DSR and CTS as requested.}
- {; If those status lines aren't ready, turn off write interrupts and}
- {; try later, after a line status change.}
- {;}
- /$F6/$06/>ASYNC_DO_DSR/$01 { TEST BYTE [<Async_Do_DSR],1 ;See if DSR checking required}
- /$74/$09 { JZ Int2b ;No -- skip it}
- {;}
- /$8B/$16/>ASYNC_UART_MSR { MOV DX,[>Async_Uart_MSR] ;Get modem status register}
- /$EC { IN AL,DX}
- /$A8/<ASYNC_DSR { TEST AL,<Async_DSR ;Check for Data Set Ready}
- /$74/$2E { JZ Int2e ;If not DSR, turn off write interrupts}
- {;}
- /$F6/$06/>ASYNC_DO_CTS/$01 {Int2b: TEST BYTE [<Async_Do_CTS],1 ;See if CTS checking required}
- /$74/$09 { JZ Int2c ;No -- skip it}
- {;}
- /$8B/$16/>ASYNC_UART_MSR { MOV DX,[>Async_Uart_MSR] ;Get modem status register}
- /$EC { IN AL,DX}
- /$A8/<ASYNC_CTS { TEST AL,<Async_CTS ;Check for Clear To Send}
- /$74/$1E { JZ Int2e ;If not CTS, turn off write ints}
- {;}
- {; All status lines look OK.}
- {; Send the XOFF.}
- {;}
- /$B0/<XOFF {Int2c: MOV AL,<XOFF ;Get XOFF Character}
- /$8B/$16/>ASYNC_BASE { MOV DX,[>Async_Base] ;Get transmit hold register address}
- /$EE { OUT DX,AL ;Output the XOFF}
- /$C6/$06/>ASYNC_SEND_XOFF/$00 { MOV BYTE [<Async_Send_XOFF],0 ;Turn off send XOFF flag}
- /$C6/$06/>ASYNC_XOFF_SENT/$01 { MOV BYTE [<Async_XOFF_Sent],1 ;Turn on sent XOFF flag}
- /$E9/$08/$FF { JMP NEAR Poll ;Return}
- {;}
- {; Not sending XOFF -- see if any character in buffer to be sent.}
- {;}
- /$8B/$1E/>ASYNC_OBUFFER_TAIL {Int2d: MOV BX,[>Async_OBuffer_Tail] ;Pick up output buffer pointers}
- /$3B/$1E/>ASYNC_OBUFFER_HEAD { CMP BX,[>Async_OBuffer_Head]}
- /$75/$0B { JNE Int2m ;Skip if not equal --> something to send}
- {;}
- {; If nothing to send, turn off write interrupts to avoid unnecessary}
- {; time spent handling useless THRE interrupts.}
- {;}
- /$8B/$16/>ASYNC_UART_IER {Int2e: MOV DX,[>Async_Uart_IER] ;If nothing -- or can't -- send ...}
- /$EC { IN AL,DX ;}
- /$24/$FD { AND AL,$FD ;}
- /$EE { OUT DX,AL ;... disable write interrupts}
- /$E9/$F3/$FE { JMP NEAR Poll ;}
- {;}
- {; If something to send, ensure that remote system didn't send us XOFF.}
- {; If it did, we can't send anything, so turn off write interrupts and}
- {; wait for later (after an XON has been received).}
- {;}
- /$F6/$06/>ASYNC_XOFF_RECEIVED/$01 {Int2m: TEST BYTE [<Async_XOFF_Received],1 ;See if we received XOFF}
- /$75/$EE { JNZ Int2e ;Yes -- can't send anything now}
- {;}
- {; If we can send character, check DSR and CTS as requested.}
- {; If those status lines aren't ready, turn off write interrupts and}
- {; try later, after a line status change.}
- {;}
- /$8B/$16/>ASYNC_UART_MSR { MOV DX,[>Async_Uart_MSR] ;Otherwise get modem status}
- /$EC { IN AL,DX}
- /$A2/>ASYNC_MODEM_STATUS { MOV [>Async_Modem_Status],AL ;and save modem status for later}
- {;}
- /$F6/$06/>ASYNC_DO_DSR/$01 { TEST BYTE [<Async_Do_DSR],1 ;See if DSR checking required}
- /$74/$04 { JZ Int2n ;No -- skip it}
- {;}
- /$A8/<ASYNC_DSR { TEST AL,<Async_DSR ;Check for Data Set Ready}
- /$74/$DB { JZ Int2e ;If not DSR, turn off write ints}
- {;}
- /$F6/$06/>ASYNC_DO_CTS/$01 {Int2n: TEST BYTE [<Async_Do_CTS],1 ;See if CTS checking required}
- /$74/$04 { JZ Int2o ;No -- skip it}
- {;}
- /$A8/<ASYNC_CTS { TEST AL,<Async_CTS ;Check for Clear To Send}
- /$74/$D0 { JZ Int2e ;If not CTS, turn off write ints}
- {;}
- {; Everything looks OK for sending, so send the character.}
- {;}
- /$C4/$3E/>ASYNC_OBUFFER_PTR {Int2o: LES DI,[>Async_OBuffer_Ptr] ;Get output buffer pointer}
- /$01/$DF { ADD DI,BX ;Position to character to output}
- /$26/$8A/$05 { ES: MOV AL,[DI] ;Get character to output}
- /$8B/$16/>ASYNC_BASE { MOV DX,[>Async_Base] ;Get transmit hold register address}
- /$EE { OUT DX,AL ;Output the character}
- {;}
- /$FF/$0E/>ASYNC_OBUFFER_USED { DEC WORD [>Async_OBuffer_Used] ;Decrement count of chars in buffer}
- /$43 { INC BX ;Increment tail pointer}
- /$3B/$1E/>ASYNC_OBUFFER_SIZE { CMP BX,[>Async_OBuffer_Size] ;See if past end of buffer}
- /$7E/$02 { JLE Int2z}
- /$31/$DB { XOR BX,BX ;If so, wrap to front}
- {;}
- /$89/$1E/>ASYNC_OBUFFER_TAIL {Int2z: MOV [>Async_OBuffer_Tail],BX ;Store updated buffer tail}
- /$E9/$AC/$FE { JMP NEAR Poll}
- {;}
- {; --- Line status change ---}
- {;}
- /$3C/$06 {Int6: CMP AL,6 ;Check for line status interrupt}
- /$75/$11 { JNE Int0 ;No -- skip.}
- {;}
- /$8B/$16/>ASYNC_UART_LSR { MOV DX,[>Async_Uart_LSR] ;Yes -- pick up line status register}
- /$EC { IN AL,DX ;and its contents}
- /$24/$1E { AND AL,$1E ;Strip unwanted bits}
- /$A2/>ASYNC_LINE_STATUS { MOV [>Async_Line_Status],AL ;Store for future reference}
- /$08/$06/>ASYNC_LINE_ERROR_FLAGS { OR [>Async_Line_Error_Flags],AL ;Add to any past transgressions}
- /$E9/$97/$FE { JMP NEAR Poll}
- {;}
- {; --- Modem status change ---}
- {;}
- /$3C/$00 {Int0: CMP AL,0 ;Check for modem status change}
- /$74/$03 { JE Int0a ;Yes -- handle it}
- /$E9/$90/$FE { JMP NEAR Poll ;Else get next interrupt}
- {;}
- /$8B/$16/>ASYNC_UART_MSR {Int0a: MOV DX,[>Async_Uart_MSR] ;Pick up modem status reg. address}
- /$EC { IN AL,DX ;and its contents}
- /$A2/>ASYNC_MODEM_STATUS { MOV [>Async_Modem_Status],AL ;Store for future reference}
- /$E8/$03/$00 { CALL EnabWI ;Turn on write interrupts, in case}
- {; ;status change resulted from CTS/DSR}
- {; ;changing state.}
- /$E9/$82/$FE { JMP NEAR Poll}
- {;}
- {; Internal subroutine to enable write interrupts.}
- {;}
- {EnabWI: ;PROC NEAR}
- /$8B/$16/>ASYNC_UART_IER { MOV DX,[>Async_Uart_IER] ;Get interrupt enable register}
- /$EC { IN AL,DX ;Check contents of IER}
- /$A8/$02 { TEST AL,2 ;See if write interrupt enabled}
- /$75/$03 { JNZ EnabRet ;Skip if so}
- /$0C/$02 { OR AL,2 ;Else enable write interrupts ...}
- /$EE { OUT DX,AL ;... by rewriting IER contents}
- /$C3 {EnabRet: RET ;Return to caller}
- {;}
- {; Send non-specific EOI to 8259 controller.}
- {;}
- /$B0/$20 {Back: MOV AL,$20 ;EOI = $20}
- /$E6/$20 { OUT $20,AL}
- {;}
- {; Restore registers}
- {;}
- /$5F { POP DI}
- /$07 { POP ES}
- /$5E { POP SI}
- /$1F { POP DS}
- /$5A { POP DX}
- /$59 { POP CX}
- /$5B { POP BX}
- /$58 { POP AX}
- /$89/$EC { MOV SP,BP}
- /$5D { POP BP}
- /$CF { IRET}
- );
-
- END (* Async_Isr *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Init --- Initialize Asynchronous Variables *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE Async_Init( Async_Buffer_Max : INTEGER;
- Async_OBuffer_Max : INTEGER;
- Async_High_Lev1 : INTEGER;
- Async_High_Lev2 : INTEGER;
- Async_Low_Lev : INTEGER );
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: Async_Init *)
- (* *)
- (* Purpose: Initializes variables *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Async_Init( Async_Buffer_Max : INTEGER; *)
- (* Async_OBuffer_Max : INTEGER; *)
- (* Async_High_Lev1 : INTEGER; *)
- (* Async_High_Lev2 : INTEGER; *)
- (* Async_Low_Lev : INTEGER ); *)
- (* *)
- (* Calls: None *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- (* STRUCTURED *) CONST
-
- Default_Com_Base : ARRAY[1..MaxComPorts] OF INTEGER =
- ( COM1_Base, COM2_Base, COM3_Base, COM4_Base );
-
- Default_Com_Irq : ARRAY[1..MaxComPorts] OF INTEGER =
- ( COM1_Irq, COM2_Irq, COM3_Irq, COM4_Irq );
-
- Default_Com_RS232: ARRAY[1..MaxComPorts] OF INTEGER =
- ( COM1_RS232, COM2_RS232, COM3_RS232, COM4_RS232 );
-
- VAR
- I: INTEGER;
-
- BEGIN (* Async_Init *)
- (* Save data segment address in code *)
- (* segment for use in serial port *)
- (* interrupt handler. *)
- Async_DSeg_Save := DSeg;
-
- (* No port open yet. *)
- Async_Open_Flag := FALSE;
-
- (* No XON/XOFF handling yet. *)
- Async_XOFF_Sent := FALSE;
- Async_XOFF_Received := FALSE;
- Async_XOFF_Rec_Display := FALSE;
- Async_XON_Rec_Display := FALSE;
- Async_Send_XOFF := FALSE;
-
- (* Set up empty receive buffer *)
- Async_Buffer_Overflow := FALSE;
- Async_Buffer_Used := 0;
- Async_MaxBufferUsed := 0;
- Async_Buffer_Head := 0;
- Async_Buffer_Tail := 0;
- (* Set up empty send buffer. *)
- Async_OBuffer_Overflow := FALSE;
- Async_OBuffer_Used := 0;
- Async_MaxOBufferUsed := 0;
- Async_OBuffer_Head := 0;
- Async_OBuffer_Tail := 0;
- (* Set default wait time for output *)
- (* buffer to drain when it fills up. *)
- Async_Output_Delay := 500;
-
- (* No modem or line errors yet. *)
- Async_Line_Status := 0;
- Async_Modem_Status := 0;
- Async_Line_Error_Flags := 0;
-
- (* Get buffer sizes *)
-
- IF ( Async_Buffer_Max > 0 ) THEN
- Async_Buffer_Size := Async_Buffer_Max - 1
- ELSE
- Async_Buffer_Size := 4095;
-
- IF ( Async_OBuffer_Max > 0 ) THEN
- Async_OBuffer_Size := Async_OBuffer_Max - 1
- ELSE
- Async_OBuffer_Size := 1131;
-
- (* Get receive buffer overflow *)
- (* check-points. *)
- IF ( Async_Low_Lev > 0 ) THEN
- Async_Buffer_Low := Async_Low_Lev
- ELSE
- Async_Buffer_Low := Async_Buffer_Size DIV 4;
-
- IF ( Async_High_Lev1 > 0 ) THEN
- Async_Buffer_High := Async_High_Lev1
- ELSE
- Async_Buffer_High := ( Async_Buffer_Size DIV 4 ) * 3;
-
- IF ( Async_High_Lev2 > 0 ) THEN
- Async_Buffer_High_2 := Async_High_Lev2
- ELSE
- Async_Buffer_High_2 := ( Async_Buffer_Size DIV 10 ) * 9;
-
- (* Allocate buffers *)
-
- GETMEM( Async_Buffer_Ptr , Async_Buffer_Size + 1 );
- GETMEM( Async_OBuffer_Ptr , Async_OBuffer_Size + 1 );
-
- (* No UART addresses defined yet *)
- Async_Uart_IER := 0;
- Async_Uart_IIR := 0;
- Async_Uart_MSR := 0;
- Async_Uart_LSR := 0;
- (* Set default port addresses *)
- (* and default IRQ lines *)
- FOR I := 1 TO MaxComPorts DO
- BEGIN
- Com_Base[I] := Default_Com_Base [I];
- Com_Irq [I] := Default_Com_Irq [I];
- Com_RS232[I] := Default_Com_RS232[I];
- END;
-
- END (* Async_Init *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Close --- Close down communications interrupts *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE Async_Close( Drop_DTR: BOOLEAN );
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: Async_Close *)
- (* *)
- (* Purpose: Resets interrupt system when UART interrupts *)
- (* are no longer needed. *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Async_Close( Drop_DTR : BOOLEAN ); *)
- (* *)
- (* Drop_DTR --- TRUE to drop DTR when closing down port *)
- (* *)
- (* Calls: None *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- VAR
- I : INTEGER;
- M : INTEGER;
-
- BEGIN (* Async_Close *)
-
- IF Async_Open_Flag THEN
- BEGIN
-
- (* disable the IRQ on the 8259 *)
-
- INLINE($FA); (* disable interrupts *)
-
- I := Port[I8088_IMR]; (* get the interrupt mask register *)
- M := 1 SHL Async_Irq; (* set mask to turn off interrupt *)
- Port[I8088_IMR] := I OR M;
-
- (* disable the 8250 interrupts *)
-
- Port[UART_IER + Async_Base] := 0;
-
- (* Disable OUT2, RTS, OUT1 on the 8250, but *)
- (* possibly leave DTR enabled. *)
-
- IF Drop_Dtr THEN
- Port[UART_MCR + Async_Base] := 0
- ELSE
- Port[UART_MCR + Async_Base] := 1;
-
- INLINE($FB); (* enable interrupts *)
-
- (* re-initialize our data areas so we know *)
- (* the port is closed *)
-
- Async_Open_Flag := FALSE;
- Async_XOFF_Sent := FALSE;
-
- (* Restore the previous interrupt pointers *)
-
- DOS_Set_Intrpt( Async_Irq + 8 , Async_Save_Iaddr[1],
- Async_Save_Iaddr[2] );
-
- END;
-
- END (* Async_Close *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Clear_Errors --- Reset pending errors in async port *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE Async_Clear_Errors;
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: Async_Clear_Errors *)
- (* *)
- (* Purpose: Resets pending errors in async port *)
- (* *)
- (* Calling sequence: *)
- (* *)
- (* Async_Clear_Errors; *)
- (* *)
- (* Calls: None *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- VAR
- I: INTEGER;
- M: INTEGER;
-
- BEGIN (* Async_Clear_Errors *)
-
- (* Read the RBR and reset any pending error conditions. *)
- (* First turn off the Divisor Access Latch Bit to allow *)
- (* access to RBR, etc. *)
-
- INLINE($FA); (* disable interrupts *)
-
- Port[UART_LCR + Async_Base] := Port[UART_LCR + Async_Base] AND $7F;
-
- (* Read the Line Status Register to reset any errors *)
- (* it indicates *)
-
- I := Port[UART_LSR + Async_Base];
-
- (* Read the Receiver Buffer Register in case it *)
- (* contains a character *)
-
- I := Port[UART_RBR + Async_Base];
-
- (* enable the irq on the 8259 controller *)
-
- I := Port[I8088_IMR]; (* get the interrupt mask register *)
- M := (1 SHL Async_Irq) XOR $00FF;
-
- Port[I8088_IMR] := I AND M;
-
- (* enable OUT2 on 8250 *)
-
- I := Port[UART_MCR + Async_Base];
- Port[UART_MCR + Async_Base] := I OR $0B;
-
- (* enable the data ready interrupt on the 8250 *)
-
- Port[UART_IER + Async_Base] := $0F;
-
- (* Re-enable 8259 *)
-
- Port[$20] := $20;
-
- INLINE($FB); (* enable interrupts *)
-
- END (* Async_Clear_Errors *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Reset_Port --- Set/reset communications port parameters *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE Async_Reset_Port( ComPort : INTEGER;
- BaudRate : INTEGER;
- Parity : CHAR;
- WordSize : INTEGER;
- StopBits : INTEGER );
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: Async_Reset_Port *)
- (* *)
- (* Purpose: Resets communications port *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Async_Reset_Port( ComPort : INTEGER; *)
- (* BaudRate : INTEGER; *)
- (* Parity : CHAR; *)
- (* WordSize : INTEGER; *)
- (* StopBits : INTEGER); *)
- (* *)
- (* ComPort --- which port (1, 2, 3) *)
- (* BaudRate --- Baud rate (110 to 19200) *)
- (* Parity --- "E" for even, "O" for odd, "N" for none *)
- (* WordSize --- Bits per character (5 through 8) *)
- (* StopBits --- How many stop bits (1 or 2) *)
- (* *)
- (* Calls: *)
- (* *)
- (* Async_Clear_Errors --- Clear async line errors *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- CONST (* Baud Rate Constants *)
-
- Async_Num_Bauds = 9;
-
- Async_Baud_Table : ARRAY [1..Async_Num_Bauds] OF RECORD
- Baud, Bits : INTEGER;
- END
-
- = ( ( Baud: 110; Bits: $00 ),
- ( Baud: 150; Bits: $20 ),
- ( Baud: 300; Bits: $40 ),
- ( Baud: 600; Bits: $60 ),
- ( Baud: 1200; Bits: $80 ),
- ( Baud: 2400; Bits: $A0 ),
- ( Baud: 4800; Bits: $C0 ),
- ( Baud: 9600; Bits: $E0 ),
- ( Baud: 19200; Bits: $E0 ) );
-
- VAR
- I : INTEGER;
- M : INTEGER;
- ComParm : INTEGER;
-
- BEGIN (* Async_Reset_Port *)
-
- (*---------------------------------------------------*)
- (* Build the ComParm for RS232_Init *)
- (* See Technical Reference Manual for description *)
- (*---------------------------------------------------*)
-
- (* Set up the bits for the baud rate *)
-
- IF ( BaudRate > Async_Baud_Table[Async_Num_Bauds].Baud ) THEN
- BaudRate := Async_Baud_Table[Async_Num_Bauds].Baud
-
- ELSE IF ( BaudRate < Async_Baud_Table[1].Baud ) THEN
- BaudRate := Async_Baud_Table[1].Baud;
-
- (* Remember baud rate for purges *)
- Async_Baud_Rate := BaudRate;
-
- I := 0;
-
- REPEAT
- I := I + 1
- UNTIL ( ( I >= Async_Num_Bauds ) OR
- ( BaudRate = Async_Baud_Table[I].Baud ) );
-
- ComParm := Async_Baud_Table[I].Bits;
-
- (* Choose Parity *)
- CASE UpCase(Parity) OF
- 'E' : ComParm := ComParm OR $0018;
- 'O' : ComParm := ComParm OR $0008;
- 'M' : ComParm := ComParm OR $0020;
- 'S' : ComParm := ComParm OR $0038;
- ELSE ;
- END (* CASE *);
- (* Choose number of data bits *)
-
- WordSize := WordSize - 5;
-
- IF ( WordSize < 0 ) OR ( WordSize > 3 ) THEN
- WordSize := 3;
-
- ComParm := ComParm OR WordSize;
-
- (* Choose stop bits *)
-
- IF StopBits = 2 THEN
- ComParm := ComParm OR $0004; (* default is 1 stop bit *)
-
- (* Use the BIOS COM port init routine *)
-
- BIOS_RS232_Init( ComPort - 1 , ComParm );
-
- (* If > 9600 baud, we have to screw *)
- (* around a bit *)
-
- IF ( BaudRate = 19200 ) THEN
- BEGIN
-
- I := PORT[ UART_LCR + Async_Base ];
- PORT[ UART_LCR + Async_Base ] := I OR $80;
-
- PORT[ UART_THR + Async_Base ] := 6;
- PORT[ UART_IER + Async_Base ] := 0;
-
- I := PORT[ UART_LCR + Async_Base ];
- PORT[ UART_LCR + Async_Base ] := I AND $7F;
-
- END;
- (* Clear any pending errors on *)
- (* async line *)
- Async_Clear_Errors;
-
- END (* Async_Reset_Port *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Open --- Open communications port *)
- (*----------------------------------------------------------------------*)
-
- FUNCTION Async_Open( ComPort : INTEGER;
- BaudRate : INTEGER;
- Parity : CHAR;
- WordSize : INTEGER;
- StopBits : INTEGER ) : BOOLEAN;
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Function: Async_Open *)
- (* *)
- (* Purpose: Opens communications port *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Flag := Async_Open( ComPort : INTEGER; *)
- (* BaudRate : INTEGER; *)
- (* Parity : CHAR; *)
- (* WordSize : INTEGER; *)
- (* StopBits : INTEGER) : BOOLEAN; *)
- (* *)
- (* ComPort --- which port (1 though 3) *)
- (* BaudRate --- Baud rate (110 to 19200) *)
- (* Parity --- "E" for even, "O" for odd, "N" for none, *)
- (* "S" for space, "M" for mark. *)
- (* WordSize --- Bits per character (5 through 8) *)
- (* StopBits --- How many stop bits (1 or 2) *)
- (* *)
- (* Flag returned TRUE if port initialized successfully; *)
- (* Flag returned FALSE if any errors. *)
- (* *)
- (* Calls: *)
- (* *)
- (* Async_Reset_Port --- initialize RS232 port *)
- (* DOS_Set_Intrpt --- set address of RS232 interrupt routine *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- BEGIN (* Async_Open *)
- (* If port open, close it down first. *)
- IF Async_Open_Flag THEN
- Async_Close( FALSE );
- (* Choose communications port *)
-
- IF ( ComPort < 1 ) THEN
- ComPort := 1
- ELSE IF ( ComPort > MaxComPorts ) THEN
- ComPort := MaxComPorts;
-
- Async_Port := ComPort;
- Async_Base := Com_Base [ ComPort ];
- Async_Irq := Com_Irq [ ComPort ];
- Async_RS232 := Com_RS232[ ComPort ];
-
- (* Set register pointers for ISR routine *)
-
- Async_Uart_IER := Async_Base + UART_IER;
- Async_Uart_IIR := Async_Base + UART_IIR;
- Async_Uart_MSR := Async_Base + UART_MSR;
- Async_Uart_LSR := Async_Base + UART_LSR;
-
- (* Check if given port installed *)
-
- IF (Port[UART_IIR + Async_Base] and $00F8) <> 0 THEN
- Async_Open := FALSE (* Serial port not installed *)
- ELSE
- BEGIN (* Open the port *)
-
- (* Get current interrupt address *)
-
- DOS_Get_Intrpt( Async_Irq + 8 , Async_Save_Iaddr[1],
- Async_Save_Iaddr[2] );
-
- (* Set interrupt routine address *)
-
- DOS_Set_Intrpt( Async_Irq + 8 , CSeg , Ofs( Async_Isr ) );
-
- (* Set up UART *)
-
- Async_Reset_Port( ComPort, BaudRate, Parity, WordSize, StopBits );
-
- Async_Open := TRUE;
- Async_Open_Flag := TRUE;
-
- END;
-
- END (* Async_Open *);
-